// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.linqingying.gitcode.authentication.ui

import com.intellij.collaboration.auth.ui.LazyLoadingAccountsDetailsProvider
import com.intellij.collaboration.auth.ui.cancelOnRemoval
import com.intellij.collaboration.messages.CollaborationToolsBundle
import com.intellij.collaboration.ui.ExceptionUtil
import com.intellij.openapi.components.service
import com.intellij.openapi.progress.ProgressManager


import icons.CollaborationToolsIcons
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.future.asDeferred
import kotlinx.coroutines.withContext
import com.linqingying.gitcode.api.GitCodeApiRequestExecutor
import com.linqingying.gitcode.api.GitCodeApiRequests
import com.linqingying.gitcode.api.data.GitCodeAuthenticatedUser
import com.linqingying.gitcode.api.data.GitCodeUserDetailed
import com.linqingying.gitcode.authentication.accounts.GCAccountManager
import com.linqingying.gitcode.authentication.accounts.GitCodeAccount
import com.linqingying.gitcode.coroutineToIndicator
import com.linqingying.gitcode.exceptions.GitCodeAuthenticationException
import com.linqingying.gitcode.util.CachingGCUserAvatarLoader
import java.awt.Image

internal class GCAccountsDetailsProvider(
  scope: CoroutineScope,
  private val executorSupplier: suspend (GitCodeAccount) -> GitCodeApiRequestExecutor?
) : LazyLoadingAccountsDetailsProvider<GitCodeAccount, GitCodeUserDetailed>(scope, CollaborationToolsIcons.Review.DefaultAvatar) {

  constructor(scope: CoroutineScope, accountManager: GCAccountManager, accountsModel: GCAccountsListModel)
    : this(scope, { getExecutor(accountManager, accountsModel, it) }) {
    cancelOnRemoval(accountsModel.accountsListModel)
  }

  constructor(scope: CoroutineScope, accountManager: GCAccountManager)
    : this(scope, { getExecutor(accountManager, it) }) {
    cancelOnRemoval(scope, accountManager)
  }

  override suspend fun loadDetails(account: GitCodeAccount): Result<GitCodeUserDetailed> {
    val executor = try {
      executorSupplier(account)
    }
    catch (e: Exception) {
      null
    }
    if (executor == null) return Result.Error(CollaborationToolsBundle.message("account.token.missing"), true)
    return withContext(Dispatchers.IO) {

      coroutineToIndicator {
        doLoadDetails(executor, account)
      }
    }
  }

  private fun doLoadDetails(executor: GitCodeApiRequestExecutor, account: GitCodeAccount)
    : Result<GitCodeAuthenticatedUser> {

    val details = try {
      executor.execute(ProgressManager.getInstance().progressIndicator,
                       GitCodeApiRequests.CurrentUser.get(account.server))
    }
    catch (e: Throwable) {
      val errorMessage = ExceptionUtil.getPresentableMessage(e)
      val needReLogin = e is GitCodeAuthenticationException
      return Result.Error(errorMessage, needReLogin)
    }

    return Result.Success(details)
  }

  override suspend fun loadAvatar(account: GitCodeAccount, url: String): Image? {
    val apiExecutor = executorSupplier(account) ?: return null
    return CachingGCUserAvatarLoader.getInstance().requestAvatar(apiExecutor, url).asDeferred().await()
  }

  companion object {
    private suspend fun getExecutor(accountManager: GCAccountManager, accountsModel: GCAccountsListModel, account: GitCodeAccount)
      : GitCodeApiRequestExecutor? {
      return accountsModel.newCredentials.getOrElse(account) {
        accountManager.findCredentials(account)
      }?.let { token ->
        service<GitCodeApiRequestExecutor.Factory>().create(account.server, token)
      }
    }

    private suspend fun getExecutor(accountManager: GCAccountManager, account: GitCodeAccount)
      : GitCodeApiRequestExecutor? {
      return accountManager.findCredentials(account)?.let { token ->
        service<GitCodeApiRequestExecutor.Factory>().create(account.server, token)
      }
    }
  }
}
